#include <cstring>
#include "BufferStream.h"

namespace miniproto
{

BufferStream::BufferStream(void *data, int size)
{
	this->data_ = static_cast<uint8_t *>(data);
	this->total_size_ = size;
	this->wbegin_ = static_cast<uint8_t *>(data);
	this->wend_ = static_cast<uint8_t *>(data);
	this->rbegin_ = static_cast<uint8_t *>(data);
	this->rend_ = static_cast<uint8_t *>(data);
}
BufferStream::~BufferStream()
{ /*NULL*/ }

void *BufferStream::read_ptr()
{
	return this->rend_;
}

int BufferStream::left_read_size()
{
	return std::max(this->total_size_ - this->has_read_size(), 0);
}

int BufferStream::has_read_size()
{
	return (this->rend_ - this->rbegin_);
}

bool BufferStream::read_buffer(int need_size)
{
	if (need_size <= this->left_read_size())
	{
		this->rend_ += need_size;
		return true;
	}
	return false;
}

bool BufferStream::read_tag(uint32_t *tag)
{
	uint64_t val = 0;
	if (read_varint64(&val) == false)
		return false;
	*tag = static_cast<uint32_t>(val);
	return true;
}

bool BufferStream::read_varint64(uint64_t *val)
{
	uint64_t b = 0, result = 0;
	uint8_t *begin = this->rend_;
	uint8_t *end = this->data_ + this->total_size_;
	for (int i = 0; i < 10 && begin != end; ++i)
	{
		b = *(begin++);

		result |= ((b & 0x7F) << (i * 7));
		if (!(b & 0x80))
			break;
		if (begin == end && i < 9)
			return false;
	}
	*val = result;
	this->rend_ = begin;
	return true;
}

bool BufferStream::read_string(::std::string *val, const uint64_t size)
{
	if (!(val->empty()))
		val->clear();
	if (uint64_t(this->left_read_size()) < size)
		return false;
	val->append((char *)(this->rend_), size);
	this->rend_ += size;
	return true;
}

bool BufferStream::read_double(double *val)
{
	if (this->left_read_size() < 8)
		return false;

	*val = 0;
	uint8_t *begin = (uint8_t *)(val);
	::memcpy(begin, this->rbegin_, 8);
	this->rend_ += 8;
	return true;
}

bool BufferStream::read_float(float *val)
{
	if (this->left_read_size() < 4)
		return false;

	*val = 0;
	uint8_t *begin = (uint8_t *)(val);
	::memcpy(begin, this->rbegin_, 4);
	this->rend_ += 4;
	return true;
}

void *BufferStream::write_ptr()
{
	return this->wend_;
}

int BufferStream::left_write_size()
{
	return std::max(this->total_size_ - this->has_write_size(), 0);
}

int BufferStream::has_write_size()
{
	return (this->wend_ - this->wbegin_);
}

bool BufferStream::write_buffer(int write_size)
{
	if (write_size <= this->left_write_size())
	{
		this->wend_ += write_size;
		return true;
	}
	return false;
}

bool BufferStream::write_tag(const uint32_t tag)
{
	return this->write_varint64(tag);
}

bool BufferStream::write_varint64(const uint64_t val)
{
	uint64_t cur_val = val;
	uint8_t *begin = this->wend_, b = 0;
	uint8_t *end = this->data_ + this->total_size_;
	for (int i = 0; i < 10 && begin != end; ++i)
	{
		b = (cur_val & 0x07F);
		cur_val >>= 7;
		if (cur_val != 0)
			b |= 0x080;
		*(begin++) = b;
		if (cur_val == 0)
			break;
		if (begin == end && i < 9)
			return false;
	}
	this->wend_ = begin;
	return true;
}

bool BufferStream::write_string(const ::std::string *val)
{
	uint64_t size = val->size();
	if (this->write_varint64(size) == false)
		return false;
	if (uint64_t(this->left_write_size()) < size)
		return false;
	::memcpy(this->wend_, val->data(), size);
	this->wend_ += size;
	return true;
}

bool BufferStream::write_double(const double val)
{
	if (this->left_write_size() < 8)
		return false;
	const uint8_t *b = (const uint8_t *)(&val);
	::memcpy(this->wend_, b, 8);
	this->wend_ += 8;
	return true;
}

bool BufferStream::write_float(const float val)
{
	if (this->left_write_size() < 4)
		return false;
	const uint8_t *b = (const uint8_t *)(&val);
	::memcpy(this->wend_, b, 4);
	this->wend_ += 4;
	return true;
}

}
